<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>html5游戏</title>
  <script src="./lufylegend-1.10.1.min.js"></script>
  <script src="./lufylegend.ui-0.14.1.min.js"></script>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
  </style>
</head>

<body>
  <!-- http://lufylegend.com/api/zh_CN/out/index.html -->
  <!-- H5开源游戏引擎 -->
  <!-- lufylegend库件的使用流程
       lufylegend库件的使用方法的使用非常简单：
       1，从http://lufylegend.com/lufylegend下载库件最新版。
       压缩包内包含lufylegend-x.x.x.js和lufylegend-x.x.x.min.js两个完整版本，还有lufylegend-x.x.x.simple.js和lufylegend-x.x.x.simple.min.js两个缩减版本，***.min是去除了回车和空格的压缩版本。
       simple缩减版本与完整版本的区别在于，将LBox2d，LQuadTree，LTransitionManager，LoadingSample1，LoadingSample2，LoadingSample3，LoadingSample4，LoadingSample5等几个类与引擎的常用部分分离，缩减了引擎的体积。如果用到了被分离的部分的功能的话，可以进行手动加载。
       2，在html中将库件引入。
       3，调用LInit函数，初始化库件。 
  -->
  <div id="legend"></div>
</body>

</html>
<script>
  // 编码逻辑：加载->点击开始->背景->人物->物品->人物逻辑->物品逻辑->积分->积分逻辑->游戏结束

  // 定义变量和素材
  var imgData = [
    { name: "back", path: "./images/back.jpg" },
    { name: "player", path: "./images/player.png" },
    { name: "item0", path: "./images/item0.png" },
    { name: "item1", path: "./images/item1.png" },
    { name: "item2", path: "./images/item2.png" },
    { name: "item3", path: "./images/item3.png" },
    { name: "item4", path: "./images/item4.png" },
    { name: "item5", path: "./images/item5.png" },
    { name: "item6", path: "./images/item6.png" },
    { name: "item7", path: "./images/item7.png" }
  ];

  // 图片素材全局变量（方便反复使用）
  var imglist
  // 新建一些图层的对象
  //  游戏加载层    背景层      人物层       物体层     结束层
  var loadLayer, backLayer, playerLayer, itemLayer, overLayer
  // 对象变量
  var hero
  // 人物动作数组 记步器
  var step = 20, stepindex = 0
  // 积分变量
  var point = 0, pointTxt
  // 生命值的变量
  var hp = 3, hpTxt
  // 暂停
  var stopState = true

  /*
    1、加载素材
    2、背景渲染
    3、人物渲染
  */

  // ------ 加载层自适应屏幕宽高 ------
  var width
  var height
  // 定义事件侦听器函数
  function watchWindowSize() {
    // 获取窗口的宽度和高度，不包括滚动条
    width = document.documentElement.clientWidth - 4;
    height = document.documentElement.clientHeight - 4;
    location.reload();
    LInit(90, "legend", width, height, main);
  }
  // 定义一个防抖函数（减少监听事件触发频率）
  var debounce = (fn, delay) => {
    let timer;
    return function () {
      if (timer) {
        clearTimeout(timer);
      }
      timer = setTimeout(() => {
        fn();
      }, delay);
    }
  }
  var cancalDebounce = debounce(watchWindowSize, 500)
  window.addEventListener("resize", cancalDebounce);

  //用window的onload事件，窗体加载完毕的时候
  window.onload = function () {
    width = document.documentElement.clientWidth - 4;
    height = document.documentElement.clientHeight - 4;
    LInit(90, "legend", width, height, main);
  }
  /* 
     调用LInit函数，初始化库件。（lufylegend库件的使用流程第三步）
     LInit ( speed  divid  width  height  callback  type ) 
     speed 游戏速度,每次页面刷新间隔（单位毫秒）, FPS = 1000 / speed。
     divid 传入一个div的id，库件进行初始化的时候，会自动将canvas加入到此div内部。
     width 游戏界面宽。
     height 游戏界面高。
     callback 游戏初始化后，调用此函数
     type *该参数在1.10.1之后已被删除，改为引擎内部自动判定

     LInit(90, "legend", width, height, main);
  */

  // ------ 游戏运行的主函数------
  // 回调函数
  function main() {
    // 游戏加载样式及事件
    // 新建一个游戏加载层 1 - 7 种类型样式
    loadLayer = new LoadingSample4()
    // 将进度条添加到游戏的界面上
    addChild(loadLayer)
    // 加载素材 
    /* LLoadManage类是可以用来同时读取图片，文本以及js多种类型的文件。
       LLoadManage.load ( list  onUpdate  onComplete ) 
       list 指定的需要加载数据的数组
       onUpdate 加载过程中调用的函数，一般用来显示游戏进度
       onComplete list中全部文件加载完成时调用此函数
    */
    LLoadManage.load(imgData, function (progress) {
      //设置进度条的进度 setProgress(value)设置进度条的长度百分比 value长度百分比(0〜100)
      loadLayer.setProgress(progress)
    }, gameInit)

  }
  // 加载素材完成后进行
  // 引入所有需要用到的图片素材
  function gameInit(result) {
    // ------ 游戏标题 ------
    var GameName
    var Name = new LPanel("#FF6600", 200, 50)
    GameName = new LTextField()
    GameName.text = "大家一起接水果"
    GameName.color = "#CD8500"
    GameName.size = 80
    GameName.stroke = true;
    GameName.lineWidth = 2;
    GameName.lineColor = "#FFFFFF";
    GameName.x = (loadLayer.getWidth() - GameName.getWidth()) * 0.5
    GameName.y = (loadLayer.getHeight() - GameName.getHeight() * 8) * 0.5
    loadLayer.addChild(GameName)

    // ------ 开始游戏按钮 ------
    var title
    // 按钮初始样式
    var upState = new LPanel("#FF6600", 200, 50)
    title = new LTextField()
    title.text = "开始游戏"
    title.color = "#FFFFFF"
    title.size = 20
    title.x = (upState.getWidth() - title.getWidth()) * 0.5
    title.y = (upState.getHeight() - title.getHeight()) * 0.5
    upState.addChild(title)

    // 移动到按钮上样式
    var overState = new LPanel("#FF66007F", 200, 50)
    title = new LTextField()
    title.text = "开始游戏"
    title.color = "#FF0000"
    title.size = 20
    title.x = (upState.getWidth() - title.getWidth()) * 0.5
    title.y = (upState.getHeight() - title.getHeight()) * 0.5
    overState.addChild(title)

    // 按下按钮样式
    var downState = new LPanel("#A50101", 200, 50)
    title = new LTextField()
    title.text = "开始游戏"
    title.color = "#FF0000"
    title.size = 20
    title.x = (upState.getWidth() - title.getWidth()) * 0.5
    title.y = (upState.getHeight() - title.getHeight()) * 0.5
    downState.addChild(title)

    // 实例化按钮
    var button01 = new LButton(upState, overState, downState)
    button01.x = LGlobal.width / 2 - 100
    button01.y = LGlobal.height - 100
    loadLayer.addChild(button01)

    // 监听按钮点击事件（点击开始游戏）
    button01.addEventListener(LMouseEvent.MOUSE_UP, go)
    function go() {
      // 销毁加载进度条
      removeChild(loadLayer)


      // 将已经加载完成的素材保存在全局变量内
      imglist = result
      // 新建背景层
      backLayer = new LSprite()
      // 将图层放到页面中
      addChild(backLayer)
      // 加载背景层进来
      addBackground()

      // 添加人物 下一步人物动起来（添加动画效果）
      addPlayer()

      // 添加暂停按钮
      stop()

      //创建物体层
      itemLayer = new LSprite()
      //把物体层放到背景层里面去
      backLayer.addChild(itemLayer)

      //渲染积分栏
      addText()
      //游戏结束层
      overLayer = new LSprite()
      //将游戏结束层添加到背景层里面去
      backLayer.addChild(overLayer)

      // 添加动画播放效果
      addEvent()
    }
  }


  // ------ 背景部分 ------
  // 添加背景
  function addBackground() {
    var data = new LBitmap(new LBitmapData(imglist['back']))
    data.scaleX = 2.4
    data.scaleY = 2.4
    // 将数据塞入背景层上面
    backLayer.addChild(data)
  }


  // ------ 人物部分 ------
  // 添加人物
  function addPlayer() {
    // 创建人物层
    playerLayer = new LSprite()

    // 实例化一个人物对象
    hero = new Player()

    hero.x = (window.innerWidth - 4) / 2 - 32
    hero.y = (window.innerHeight - 4) - 84

    // 将人物放入人物层中
    playerLayer.addChild(hero)

    // 将人物层放到背景层中
    backLayer.addChild(playerLayer)
  }
  // 构造人物对象
  function Player() {
    // 让我们的人物对象去继承图层对象
    /* base 等同于 LExtends 对象继承
       LExtends ( child  parent  params ) 
       child 子对象本身 
       parent 父对象
       params 参数
    */
    base(this, LSprite, [])

    //保存当前的this属性
    var that = this

    //人物标记一下状态
    that.mode = '' //left right

    //标记跳转状态
    that.jump = false // false 不跳跃 true跳跃

    // 将图片塞入进来 256*256 256/4=一个图片宽和高64*64
    // 可以将图片进行坐标的切割，分成一个二维数组
    // 将传入的宽和高，按照行数和列数进行拆分计算，会得到一个2维数组。
    var list = LGlobal.divideCoordinate(256, 256, 4, 4)

    // 图片数据获取
    var data = new LBitmapData(imglist['player'], 0, 0, 64, 64)

    //设置人物的动画，让人物按照我们切割好的坐标位置去运作
    that.anmiate = new LAnimation(this, data, list)

    //人物的动作位置
    that.setp = 2, that.stepindex = 0
  }
  // 人物运动事件
  Player.prototype.run = function () {
    //保存this对象
    var that = this
    if (that.mode == 'left') {
      //说明往左边走
      if (that.x < 0) {
        that.x = LGlobal.width
      } else {
        //当积分到达20或超过以后，人物移动加快（左）
        if (point >= 20) {
          that.x -= point

        } else {
          that.x -= 20
        }
      }
    } else if (that.mode == 'right') {
      //说明往右边走
      if (that.x >= LGlobal.width) {
        that.x = 0
      } else {
        //当积分到达20或超过以后，人物移动加快（右）
        if (point >= 20) {
          that.x += point

        } else {
          that.x += 20
        }
      }
    }
    //等于true就是跳跃
    if (that.jump) {
      that.jumps()
    }
    that.anmiate.onframe()
  }
  // 让人物跳跃
  Player.prototype.jumps = function () {
    var that = this
    var max = LGlobal.height - 100
    if (that.y <= max) {
      //往下走
      play('bottom')
    } else {
      //往上走
      play('top')
    }
    function play(dir) {
      if (dir == "top") {
        //往上走
        that.y -= 20
      } else {
        //往下掉
        that.y = LGlobal.height - that.getHeight()
        that.jump = false
      }
    }

  }


  // ------ 动画播放部分 ------
  // 默认动画(人物原地踏步、水果下落、重复渲染生命积分) -> 键盘(按下弹起) -> 鼠标(按下弹起右键跳跃) 
  function addEvent() {
    // 给背景层添加事件（点击背景左右两边，向左右两边运动）
    // 鼠标事件
    backLayer.addEventListener(LMouseEvent.MOUSE_DOWN, down)
    backLayer.addEventListener(LMouseEvent.MOUSE_UP, up)


    // 键盘事件
    LGlobal.stage.addEventListener(LKeyboardEvent.KEY_DOWN, keydown)
    LGlobal.stage.addEventListener(LKeyboardEvent.KEY_UP, keyup)


    // 默认动画
    backLayer.addEventListener(LEvent.ENTER_FRAME, onframe);
  }


  // ------ 默认动画部分 ------
  function onframe() {
    // 让人物动起来
    hero.run()

    // 让物体层中所有的物体全部运动
    for (var i = 0; i < itemLayer.childList.length; i++) {
      itemLayer.childList[i].run()
      // 打印每一个元素的状态
      if (itemLayer.childList[i].mode == "die") {
        //消亡掉指定元素
        itemLayer.removeChild(itemLayer.childList[i])
      }
    }
    // 间隔时间段去添加物体
    if (stepindex++ > step) {
      addItem()
      stepindex = 0
    }

    //每次要重复渲染文字显示
    showText()

    //如果生命值小于等于0就说明游戏结束
    if (hp <= 0) {
      gameOver()
    }
  }


  // ------ 键盘控制部分 ------
  // 键盘按下
  function keydown(e) {
    //获取键盘的按键 e.keyCode
    if (e.keyCode == 37) {
      //左边 
      hero.mode = 'left'
      hero.anmiate.setAction(1) //设置动画
    } else if (e.keyCode == 39) {
      //右边
      hero.mode = 'right'
      hero.anmiate.setAction(2) //设置动画
    } else if (e.keyCode == 32) {
      hero.jump = true
    }
  }
  // 鼠标弹起 
  function keyup(e) {
    if (e.keyCode == 37 || e.keyCode == 39) {
      if (!hero.jump) {
        hero.mode = ''
        hero.anmiate.setAction(0)
      }
    }
  }


  // ------ 鼠标控制部分 ------
  // 鼠标按下
  function down(e) {
    // 鼠标右键跳跃
    if (e.button == 2) {
      // 阻止右键弹框
      document.oncontextmenu = function () { return false }
      hero.jump = true
    }
    // 判断当前的鼠标位置是否小于总宽度的一半 点击左边
    if (e.selfX < LGlobal.width / 2) {
      hero.mode = 'left'
      hero.anmiate.setAction(1) //设置动画
    } else {
      hero.mode = 'right'
      hero.anmiate.setAction(2) //设置动画
    }
  }
  // 鼠标的弹起
  function up() {
    hero.mode = ''
    hero.anmiate.setAction(0)
  }


  // ------ 水果部分 ------
  // 构建一个水果对象
  function Item() {
    //继承
    base(this, LSprite, [])

    //保存当前对象
    var that = this

    //物体也要有模式
    that.mode = '' //die 消亡

    //物体要随机出现，所以要生成一个随机的下标 8个物体 0-7
    var index = Math.floor(Math.random() * 8)

    //我们就可以知道是哪一张图片，如果知道是哪一张图片就知道是加分还是减分
    that.value = index < 4 ? 1 : -1;

    //得到图片的数据
    var bitmap = new LBitmap(new LBitmapData(imglist[`item${index}`]))

    //将图片数据添加到图层上
    that.addChild(bitmap)
  }
  // 写一个运动的方法
  Item.prototype.run = function () {
    var that = this

    // 当积分超过或等于10分之后，下落速度随积分增加
    if (point >= 10) {
      that.y += point
    } else {
      that.y += 10
    }

    var hit = that.checkHit()

    // 检测是否要消亡
    if (that.y > LGlobal.height || hit) {
      that.mode = "die"
    }
  }
  // 写一个检测物体是否有碰撞的方法
  Item.prototype.checkHit = function () {
    var that = this

    //引擎提供了检测碰撞的方法 如果有碰撞就返回true 没有就false
    var result = LGlobal.hitTestArc(that, hero)

    if (result) {
      //加分减生命值
      if (that.value < 0) {
        hp--;
      } else {
        point++;
      }
    }

    return result;
  }
  // 将水果添加到物体层中
  function addItem() {
    //创建一个物体对象
    var item = new Item()

    item.y = 0

    item.x = Math.floor(Math.random() * (LGlobal.width - item.getWidth()))

    //创建的物体放到物体层
    itemLayer.addChild(item)
  }


  // ------ 文本部分 ------
  // 添加积分
  function addText() {
    //创建一个文本图层
    hpTxt = new LTextField()
    hpTxt.color = '#ff0000'
    hpTxt.size = 30
    hpTxt.x = 10
    hpTxt.y = 10
    backLayer.addChild(hpTxt)

    pointTxt = new LTextField()
    pointTxt.color = '#fff'
    pointTxt.size = 30
    pointTxt.x = 10
    pointTxt.y = 50
    backLayer.addChild(pointTxt)

    showText()
  }
  // 显示文本
  function showText() {
    hpTxt.text = `生命：${hp}`
    pointTxt.text = `积分：${point}`
  }

  // 暂停按钮
  function stop() {
    // 按钮初始样式
    var ContTxt
    var stopBtn = new LPanel("#00000030", 100, 50)
    // 按钮内部文本
    ContTxt = new LTextField()
    ContTxt.text = "暂停游戏"
    ContTxt.color = "#FFFFFF"
    ContTxt.size = 20
    ContTxt.x = (stopBtn.getWidth() - ContTxt.getWidth()) * 0.5
    ContTxt.y = (stopBtn.getHeight() - ContTxt.getHeight()) * 0.5
    stopBtn.addChild(ContTxt)
    // 实例化按钮
    var buttonStop = new LButton(stopBtn)
    buttonStop.x = LGlobal.width - buttonStop.getWidth()
    buttonStop.y = 0
    // 载入按钮
    backLayer.addChild(buttonStop)

    // 监听按钮点击事件
    buttonStop.addEventListener(LMouseEvent.MOUSE_UP, function () {
      // 创建一个文本层
      var stopTitle = new LTextField()
      stopTitle.color = '#ff0000'
      stopTitle.size = 50
      stopTitle.text = "游戏暂停"
      stopTitle.x = (LGlobal.width - stopTitle.getWidth()) / 2
      stopTitle.y = 150

      if (stopState) {
        // 让人物和物体都停下来
        backLayer.removeEventListener(LEvent.ENTER_FRAME, onframe)
        // 将文本放到背景层
        backLayer.addChild(stopTitle)
        stopState = false
      } else {
        // 移除文本层
        backLayer.removeChild(backLayer.childList[backLayer.childList.length-1])
        backLayer.addEventListener(LEvent.ENTER_FRAME, onframe);
        stopState = true
      }
    })
  }


  // ------ 游戏结束部分 ------
  //游戏结束
  function gameOver() {
    //让人物和物体都停下来
    backLayer.die()

    //移出掉物体层中所有的元素
    itemLayer.removeAllChild()

    //创建一个文本层
    var txt = new LTextField()
    txt.color = '#ff0000'
    txt.size = 50
    txt.text = "GAME OVER"
    txt.x = (LGlobal.width - txt.getWidth()) / 2
    txt.y = 150

    //将文本放到游戏结束层
    overLayer.addChild(txt)

    //在点击一下就重新开始游戏
    backLayer.addEventListener(LMouseEvent.MOUSE_DOWN, function () {
      backLayer.die()
      overLayer.removeAllChild()
      hp = 3
      point = 0
      addEvent()
      stop()
    })
  }
</script>